//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.TridentWordAddIn.Common;
using Microsoft.TridentWordAddIn.ResourceHelper;
using Microsoft.TridentWordAddIn.Server;
using System.Threading;

namespace Microsoft.TridentWordAddIn.Presentation
{
    /// <summary>
    /// Task pane for viewing, editing Server Connections.
    /// </summary>
    public partial class ConnectionsTaskPane : BaseUserControl, ITaskPaneControl, IDisposable
    {
        private bool error;
        private Thread asyncDownload;
        private List<Workflow> workflows;
        private ListViewSorting lvWorkflowsSorting;
        private ListViewSorting lvConnectionSorting;
        private int previouslySortedWorkflowsColumn;
        private int previouslySortedConnectionsColumn;

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ConnectionsTaskPane"/> class.
        /// </summary>
        public ConnectionsTaskPane()
        {
            base.Initialize(true);
            this.InitializeComponent();    
            this.ControllerInstance.OnConnectionChanged += new EventHandler<ConnectionChangedEventArgs>(Controller_OnConnectionChanged);
            this.ControllerInstance.OnDocumentClosing += new EventHandler<BaseEventArgs>(Controller_OnDocumentClosing);
            this.workflows = new List<Workflow>();
            this.InitializeListViewSorting();
            this.UpdateConnectionList(true);            
        }

        private void Controller_OnDocumentClosing(object sender, BaseEventArgs e)
        {
            if (e.DocumentKey == DocumentKey)
            {
                asyncDownload.CancelThread();
            }
        }        

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the Click event of the btnCreateConnection control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnCreateConnection_Click(object sender, System.EventArgs e)
        {           
            TridentConnection connection = null;
            this.ControllerInstance.ShowServerConnectionForm(ref connection, false, false);            
        }

        /// <summary>
        /// Handles the Click event of the btnEditConnection control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnEditConnection_Click(object sender, System.EventArgs e)
        {
            string selConnectionName = this.GetSelectedConnectionName();
            if (!selConnectionName.IsNullOrEmpty())
            {
                TridentConnection connection = this.ControllerInstance.UserData.Connections[selConnectionName];                
                this.ControllerInstance.ShowServerConnectionForm(ref connection, true, false);
            }
        }

        /// <summary>
        /// Handles the Click event of the btnSetDefaultConnection control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSetDefaultConnection_Click(object sender, EventArgs e)
        {
            string selConnectionName = this.GetSelectedConnectionName();
            if (selConnectionName != null)
            {
                TridentConnection connection = this.ControllerInstance.UserData.Connections[selConnectionName];
                TridentConnection connectionReturn = new TridentConnection();
                bool success = this.ControllerInstance.TestConnectionAndPromptIfRequired(connection, ref connectionReturn, false);
                if (success)
                {
                    this.ControllerInstance.CurrentConnection = connectionReturn;
                    this.ControllerInstance.ConnectionChanged(true, ConnectionOperation.Changed);
                }
            }
        }

        /// <summary>
        /// Handles the Click event of the btnRemoveConnection control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnRemoveConnection_Click(object sender, EventArgs e)
        {
            string selConnectionName = this.GetSelectedConnectionName();
            if (selConnectionName != null)
            {
                string confirmationMessage = string.Format(ResourceFetcher.GetString("RemoveConnectionConfirmation"), selConnectionName);
                if (MessageBoxHelper.ShowOkCancel(confirmationMessage) != DialogResult.OK)
                {
                    return;
                }

                bool isCurrentConnection = selConnectionName.Compare(this.ControllerInstance.CurrentConnectionName);
                bool isConnectionRemoved = this.ControllerInstance.RemoveConnection(selConnectionName);
                
                if (isConnectionRemoved)
                {
                    if (isCurrentConnection)
                    {
                        this.ControllerInstance.ConnectionChanged(true, ConnectionOperation.Deleted);
                        this.ControllerInstance.SetNextDefaultConnection();
                    }
                    else
                    {
                        this.ControllerInstance.ConnectionChanged(false, ConnectionOperation.Deleted);
                    }
                }
            }
        }        

        /// <summary>
        /// Handles the ItemSelectionChanged event of the lvConfiguredConnections control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.ListViewItemSelectionChangedEventArgs"/> instance containing the event data.</param>
        private void lvConfiguredConnections_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            bool enable = this.lvConfiguredConnections.SelectedItems.Count > 0;
            this.SetButtonsEnabled(enable);
            if (enable)
            {
                string selectedConnection = this.GetSelectedConnectionName();
                if (selectedConnection != null &&
                    selectedConnection.Compare(this.ControllerInstance.CurrentConnectionName))
                {
                    this.btnSetDefaultConnection.Enabled = false;
                }
            }
        }

        /// <summary>
        /// Handles the ColumnClick event of the lvWorkflowList control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.ColumnClickEventArgs"/> instance containing the event data.</param>
        private void lvWorkflowList_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // Determine if clicked column is already the column that is being sorted.
            if (e.Column == this.lvWorkflowsSorting.SortColumn)
            {
                this.lvWorkflowsSorting.SortDirection = this.lvWorkflowsSorting.SortDirection == SortOrder.Ascending ?
                    SortOrder.Descending : SortOrder.Ascending;                
            }
            else
            {
                this.lvWorkflowsSorting.SortColumn = e.Column;
                this.lvWorkflowsSorting.SortDirection = SortOrder.Ascending;
            }

            lvWorkflowList.SetSortIcon(this.lvWorkflowsSorting, ref this.previouslySortedWorkflowsColumn, e.Column);
            this.lvWorkflowList.Sort();
        }

       

        /// <summary>
        /// Handles the ColumnClick event of the lvConfiguredConnections control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.ColumnClickEventArgs"/> instance containing the event data.</param>
        private void lvConfiguredConnections_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // Determine if clicked column is already the column that is being sorted.
            if (e.Column == this.lvConnectionSorting.SortColumn)
            {
                this.lvConnectionSorting.SortDirection = this.lvConnectionSorting.SortDirection == SortOrder.Ascending ?
                    SortOrder.Descending : SortOrder.Ascending;
            }
            else
            {
                this.lvConnectionSorting.SortColumn = e.Column;
                this.lvConnectionSorting.SortDirection = SortOrder.Ascending;
            }

            lvConfiguredConnections.SetSortIcon(this.lvConnectionSorting, ref this.previouslySortedConnectionsColumn, e.Column);
            this.lvConfiguredConnections.Sort();
        }

        /// <summary>
        /// Handles the Tick event of the timerDownload control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void timerDownload_Tick(object sender, EventArgs e)
        {
            if (!this.asyncDownload.IsAlive)
            {
                this.timerDownload.Stop();
                this.DownloadCompleted();
            }
        }

        #endregion

        #region Private Members

        /// <summary>
        /// Initializes the list view sorting.
        /// </summary>
        private void InitializeListViewSorting()
        {
            this.lvWorkflowsSorting = new ListViewSorting();
            this.lvWorkflowList.ListViewItemSorter = this.lvWorkflowsSorting;
            this.previouslySortedWorkflowsColumn = -1;
            this.lvConnectionSorting = new ListViewSorting();
            this.lvConfiguredConnections.ListViewItemSorter = this.lvConnectionSorting;
            this.previouslySortedConnectionsColumn = -1;            
        }

        /// <summary>
        /// Handles the OnConnectionChanged event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Controller_OnConnectionChanged(object sender, ConnectionChangedEventArgs e)
        {
            this.UpdateConnectionList(IsConnectionUpdateNeeded);
        }

        /// <summary>
        /// Updates the connection list.
        /// </summary>
        /// <param name="updateWorkflowList">if set to <c>true</c> [update workflow list].</param>
        private void UpdateConnectionList(bool updateWorkflowList)
        {
            this.UpdateConfiguredConnections(this.ControllerInstance.UserData);

            bool firstTime = this.previouslySortedConnectionsColumn == -1;
            int column = firstTime ? 0 : this.previouslySortedConnectionsColumn;
            this.lvConfiguredConnections.SetSortIcon(this.lvConnectionSorting,
                                                     ref this.previouslySortedConnectionsColumn,
                                                     column);
            if (firstTime)
            {
                this.lvConfiguredConnections.Sort();
            }

            if (updateWorkflowList)
            {
                this.UpdateWorkflowList(this.ControllerInstance.GetConnectionByDocumentKey(DocumentKey));
            }
        }

        /// <summary>
        /// Updates the configured connections.
        /// </summary>
        /// <param name="userData">The user data.</param>
        private void UpdateConfiguredConnections(UserData userData)
        {
            this.lvConfiguredConnections.BeginUpdate();

            this.lvConfiguredConnections.Items.Clear();
            SerializableDictionary<string, TridentConnection> connectionMappings = userData.Connections;
            string currentConnection = this.ControllerInstance.GetConnectionByDocumentKey(DocumentKey);
            if (currentConnection.IsNullOrEmpty())
            {
                currentConnection = userData.CurrentConnectionName;
            }

            foreach (KeyValuePair<string, TridentConnection> connectionMapping in connectionMappings)
            {
                TridentConnection conn = connectionMapping.Value;
                string connectionName = conn.Name;                
                ListViewItem connectionItem = new ListViewItem(conn.SourceName);
                connectionItem.ImageKey = connectionName.Compare(currentConnection) ? "ActiveConnection" : "InActiveConnection";
                connectionItem.Name = connectionName;
                connectionItem.SubItems.Add(connectionName);
                this.lvConfiguredConnections.Items.Add(connectionItem);
            }

            this.SetButtonsEnabled(this.lvConfiguredConnections.SelectedItems.Count > 0);

            this.lvConfiguredConnections.EndUpdate();
        }

        /// <summary>
        /// Updates the workflow list.
        /// </summary>
        /// <param name="connectionName">Name of the connection.</param>
        private void UpdateWorkflowList(string connectionName)
        {
            if (connectionName.IsNullOrEmpty())
            {
                this.lblCurrentConnection.Text = "No Connection Configured";
                this.lblWorkflowList.Text = string.Empty;
                this.lvWorkflowList.Items.Clear();
                return;
            }

            TridentConnection connectionTest = ControllerInstance.UserData.Connections[connectionName];
            TridentConnection connectionReturn = null;
            if (!ControllerInstance.TestConnectionAndPromptIfRequired(connectionTest, ref connectionReturn, false))
            {
                return;
            }

            this.asyncDownload.CancelThread();
            this.lblCurrentConnection.Text = connectionName;
            this.lblWorkflowList.Text = "Workflows at: " + connectionName;
            this.lvWorkflowList.Visible = false;
            this.ctrJobProcessing.Visible = true;

            this.asyncDownload = new Thread(new ThreadStart(this.DownloadWorkflows));
            this.asyncDownload.Start();
            this.timerDownload.Start();
        }

        /// <summary>
        /// Downloads the workflows.
        /// </summary>
        private void DownloadWorkflows()
        {
            try
            {
                this.error = false;
                this.workflows.Clear();
                this.workflows = this.Server.Workflows;
            }
            catch (DatabaseException)
            {
                this.error = true;
            }
            catch (ConnectionFailedException)
            {
                this.error = true;
            }
            catch (ThreadAbortException)
            {
            }
            catch (Exception ex)
            {
                this.error = true;
                if (ex.Message.CompareIgnoreCase("Unknown web service failure"))
                {
                    return;
                }

                this.BeginInvoke(new ShowMessageDelegate(MessageBoxHelper.ShowException), new object[] { ex, this});
            }
        }

        /// <summary>
        /// Downloads the completed.
        /// </summary>
        private void DownloadCompleted()
        {
            this.lvWorkflowList.Visible = true;
            this.ctrJobProcessing.Visible = false;

            if (this.error)
            {
                this.lvWorkflowList.ShowErrorMessage(
                    "Error occured during retrieving the workflows. Please check configuration.");
            }
            else
            {
                this.UpdateWorkflowListView();

                bool firstTime = this.previouslySortedWorkflowsColumn == -1;
                int column = firstTime ? 0 : this.previouslySortedWorkflowsColumn;
                if (firstTime)
                {
                    this.lvWorkflowsSorting.SortDirection = SortOrder.Ascending;
                }

                lvWorkflowList.SetSortIcon(this.lvWorkflowsSorting, ref this.previouslySortedWorkflowsColumn, column);
                this.lvWorkflowList.Sort();
            }
        }

        /// <summary>
        /// Updates the workflow list view.
        /// </summary>
        private void UpdateWorkflowListView()
        {
            this.lvWorkflowList.BeginUpdate();
            this.lvWorkflowList.Items.Clear();

            if (this.workflows.Count == 0)
            {
                this.lvWorkflowList.ShowErrorMessage("No workflows present at selected connection.");
            }
            else
            {
                string[] columns = new string[] { "Workflow", "Description"};
                this.lvWorkflowList.AddColumns(columns);
                foreach (Workflow task in this.workflows)
                {
                    string workflowName = task.DisplayName.IsNullOrEmpty() ? task.Name : task.DisplayName;
                    ListViewItem newItem = new ListViewItem(workflowName);
                    newItem.ImageKey = "workflow";
                    newItem.SubItems.Add(task.Description);
                    this.lvWorkflowList.Items.Add(newItem);
                }
            }

            this.lvWorkflowList.EndUpdate();
        }

        /// <summary>
        /// Gets the name of the selected connection.
        /// </summary>
        /// <returns>string</returns>
        private string GetSelectedConnectionName()
        {
            if (this.lvConfiguredConnections.SelectedItems.Count > 0)
            {
                return this.lvConfiguredConnections.SelectedItems[0].Name;
            }

            return null;
        }

        /// <summary>
        /// Sets the buttons enabled.
        /// </summary>
        /// <param name="enabled">if set to <c>true</c> [enabled].</param>
        private void SetButtonsEnabled(bool enabled)
        {
            this.btnEditConnection.Enabled = enabled;
            this.btnRemoveConnection.Enabled = enabled;
            this.btnSetDefaultConnection.Enabled = enabled;
        }

        /// <summary>
        /// Adds the list view column.
        /// </summary>
        private void AddListViewColumn()
        {
            if (this.lvWorkflowList.Columns.Count == 1)
            {
                this.lvWorkflowList.Columns.Clear();
                this.lvWorkflowList.Columns.Add("Workflow");
                this.lvWorkflowList.Columns.Add("Description");
                this.lvWorkflowList.Columns[0].Width = 125;
                this.lvWorkflowList.Columns[1].Width = 130;
            }
        }

        #endregion

        #region ITaskPaneControl Implementation

        /// <summary>
        /// Sets the dock position.
        /// </summary>
        /// <param name="dockPosition">The dock position.</param>
        public void SetDockPosition(TaskPaneDockPositions dockPosition)
        {
            if ((dockPosition & (TaskPaneDockPositions.Left | TaskPaneDockPositions.Right | TaskPaneDockPositions.Floating)) != 0)
            {
                this.SetVertical(true);
            }
            else
            {
                this.SetVertical(false);
            }
        }

        /// <summary>
        /// Sets the vertical.
        /// </summary>
        /// <param name="controlIsVertical">if set to <c>true</c> [control is vertical].</param>
        private void SetVertical(bool controlIsVertical)
        {
            this.SuspendLayout();
            int third;
            if (controlIsVertical)
            {
                third = this.Height / 2;
            }
            else
            {
                third = this.Width / 2;
            }

            // splitcontainter orientation is opposite of the control's orientation
            this.splitContainer1.Orientation = controlIsVertical ? Orientation.Horizontal : Orientation.Vertical;
            this.splitContainer1.SplitterDistance = third;

            this.ResumeLayout();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"/>.
        /// </summary>
        void IDisposable.Dispose()
        {
            this.Disposing(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the specified the privately held resources.
        /// </summary>
        /// <param name="disposing">if set to <c>true</c> disposes resources.</param>
        private void Disposing(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    this.ControllerInstance.OnConnectionChanged -= new EventHandler<ConnectionChangedEventArgs>(Controller_OnConnectionChanged);
                    base.Disposing();
                }
            }

            this.disposed = true;
        }

        #endregion
    }
}
